import tkinter as tk
from tkinter import ttk, messagebox
from src.utils.loguru_func import app_init_logger, serial_port_logger
import time
from src.utils.serial_func import show_available_ports, get_available_ports
from src.types.common_type import Serial_Config_Type
from src.utils.serial_func import SerialCommunication
import json
from src.utils.modbus_func import Huikong_Output
from src.utils.json_edit_frame import Config_Json_Edit_Win


class Main_Window(tk.Toplevel):
    def __init__(self, root: tk.Tk, app):
        super().__init__(root)
        self.APP = app
        self.HUIKONG_OUTPUT: Huikong_Output = self.APP.HUIKONG_OUTPUT

        # FIXME 此处进行挂载，初始化Main_Window时，可以用APP相关的属性
        self.APP.MAIN_WINDOW = self

        show_available_ports()
        self.SERIAL_PORTS = get_available_ports()
        self.setup()

        # 绑定窗口关闭事件
        self.protocol("WM_DELETE_WINDOW", self.on_close)

    def on_close(self):
        """窗口关闭时的回调函数"""
        # 这里可以添加关闭窗口前需要执行的操作，例如关闭串口连接等
        # 硬件连接的清理
        for interface, item in self._2_1_1_serial_config_frame.config_item.items():
            if item.serial_config.port != "null":
                item.serial_communication.close_port()
        self.HUIKONG_OUTPUT.disConnect()
        app_init_logger.debug("窗口关闭")

        # FIXME ；关闭Toplevel前，先把主窗口关闭，避免程关闭后，任务管理器中仍然有进程对
        self.APP.ROOT.destroy()

    def setup(self):
        self.width = 1000
        self.height = 800
        self.bg_color = "#E0E0E0"

        self.geometry(f"{self.width}x{self.height}")
        self.configure(bg=self.bg_color)
        # 设置窗口标题
        self.title("控制器电路板测试工具")
        self.resizable(False, False)

        # 创建 header、body 和 footer 框架
        self.create_header()
        app_init_logger.debug("主窗口header创建完成")
        self.create_body()
        app_init_logger.debug("主窗口body创建完成")
        self.create_footer()
        app_init_logger.debug("主窗口footer创建完成")

    def create_header(self):
        # 创建 header 框架
        self._1_header_frame = tk.Frame(self, height=100)
        self._1_header_frame.pack(fill=tk.X)
        # 在 header 中添加标签

        self.ceshi_shuoming_text = tk.Text(
            self._1_header_frame, width=80, height=10)
        self.ceshi_shuoming_text.pack(pady=20)

        self.add_buzhoushuoming()

    def add_buzhoushuoming(self):
        # 将文件读取的内容显到text中
        with open("docs/测试步骤.txt", "r", encoding='utf-8') as f:
            manual_txt = f.read()
        self.ceshi_shuoming_text.insert(tk.END, manual_txt)

    def create_body(self):
        if 1:
            # 创建 body 框架
            self._2_body_frame = tk.Frame(self, bg="#FFFFFF", height=400)
            self._2_body_frame.pack(fill=tk.BOTH, expand=True)

            # 在 body 中添加标签
            # body_label = tk.Label(
            #     self._2_body_frame, text="测试用串口配置工具 - 主体",  bg="#FFFFFF", font=("Arial", 14))
            # body_label.pack(pady=20)

            if 1:
                # 创建串口配置的frame，从扫描到的串口中选择
                self._2_1_serial_config_frame = tk.LabelFrame(
                    self._2_body_frame, text="串口配置", width=800, height=200, bg="#FFFFFF")

                # 禁止LabelFrame跟随子元素调整大小
                self._2_1_serial_config_frame.pack_propagate(False)
                self._2_1_serial_config_frame.pack(side=tk.TOP)

                self._2_1_1_serial_config_frame = Serial_Config_Frame(
                    self._2_1_serial_config_frame, self.APP)
                self._2_1_1_serial_config_frame.pack(side=tk.TOP)

                # test_Label = tk.Label(
                #     self._2_1_serial_config_frame, text="测试用串口配置工具 - 主体", bg="#FFFFFF", font=("Arial", 14))
                # test_Label.pack(pady=20)

            if 1:
                self._2_2_oprater_btn_frame = tk.Frame(
                    self._2_body_frame, bg="#8B918A", height=100)

                self._2_2_oprater_btn_frame.pack(side=tk.TOP)
                # 在 footer 中添加标签
                piliang_config_btn = tk.Button(self._2_2_oprater_btn_frame, text="批量串口配置",
                                               bg="#2196F3", fg="white", font=("Arial", 24), command=self.start_config_uart)
                start_row = 1
                start_col = 1
                piliang_config_btn.grid(row=start_row, column=start_col,
                                        columnspan=2, padx=10, pady=10)

                piliang_trigger_btn = tk.Button(self._2_2_oprater_btn_frame, text="顺序触发测试",

                                                bg="#819623", fg="white", font=("Arial", 24), command=self.start_trigger_test)
                piliang_trigger_btn.grid(row=start_row, column=start_col+2,
                                         columnspan=2, padx=10, pady=10)

            if 1:
                # 创建 log_listbox的容器frame
                self._2_3_log_listbox_frame = tk.LabelFrame(
                    self._2_body_frame, text="log记录", width=800, height=200,  bg="#FFFFFF")
                self._2_3_log_listbox_frame.pack_propagate(False)
                self._2_3_log_listbox_frame.pack(side=tk.TOP)

                # # 在body_frame中创建一个Log_Listbox
                self._2_3_1log_listbox = Log_Listbox(
                    self._2_3_log_listbox_frame, self.APP)
                # # 布局靠下
                self._2_3_1log_listbox.pack(side=tk.TOP)

    def create_footer(self):
        self.step_state_lable_count = 4
        # 创建 footer 框架
        self._3_footer_frame = tk.Frame(self, height=100)
        self._3_footer_frame.pack(fill=tk.X)
        self.step_state_lable_dict = {}

        row = 1
        col = 1
        for i in range(self.step_state_lable_count):
            step_state_lable = tk.Label(
                self._3_footer_frame, text=f"step{i}", relief=tk.RAISED, font=("Arial", 14))
            step_state_lable.grid(row=row, column=col, padx=10, pady=10)
            col += 1
            self.step_state_lable_dict[i] = step_state_lable

        test_btn = tk.Button(self._3_footer_frame,
                             text="配置", command=self.config_json_edit)
        test_btn.grid(row=row+1, column=1, padx=10, pady=10)
        self.test_step_btn_count = 0

    def config_json_edit(self):
        Config_Json_Edit_Win(self, self.APP)

    def fresh_step_label_color(self, step_id, color):
        self.step_state_lable_dict[step_id].config(bg=color)

    def reset_step_label_color_all(self):
        for i in range(self.step_state_lable_count):
            self.fresh_step_label_color(i, "gray")

    def active_step_lable(self, i):
        self.reset_step_label_color_all()
        self.fresh_step_label_color(i, "green")

    def start_config_uart(self):
        for interface, item in self._2_1_1_serial_config_frame.config_item.items():
            # serial_port_logger.debug(interface)
            # serial_port_logger.debug(item.port_conf['com'])
            if item.serial_config.port != "null":

                self._2_3_1log_listbox.add_record(f"开始配置{interface}")
                serial_port_logger.debug(f"开始配置{interface}")
                # 发送数据
                item.send_data()
                time.sleep(0.1)
                # 接收数据
                received_data = item.receive_data()
                # serial_port_logger.debug(received_data)
                # serial_port_logger.debug(item.port_conf['data'])
                if item.port_conf['data'] in received_data:
                    color = 'green'
                else:
                    color = 'pink'
                    messagebox.showinfo("警告", f"测试接口{interface}返回数据不正确，请确认")

                self._2_3_1log_listbox.add_record(
                    f"测试接口{interface}的串口接收数据: {received_data}", color=color)
                # serial_port_logger.debug(
                #     f"测试接口{interface}的串口接收数据: {received_data}")

        # 配置完成后，状态再step02
        self.APP.STEP_MANAGER.step01_to_step02()

    def start_trigger_test(self):
        # 当前是否在step02
        current_step = self.APP.STEP_MANAGER.get_current_step()
        if current_step != 'step02':
            self._2_3_1log_listbox.add_record(f"当前不在step02，无法触发测试")
            return

        trigger_counts = self.HUIKONG_OUTPUT.trigger_channels_in_order()
        self._2_3_1log_listbox.add_record(f"触发次数: {trigger_counts}")

        # 开关预期次数，就进入下一步
        if trigger_counts['on'] == 20 and trigger_counts['off'] == 20:
            self.APP.STEP_MANAGER.step02_to_step03()

    # 窗口关闭时回调函数


class Log_Listbox(tk.Listbox):
    def __init__(self, master: tk.LabelFrame, app, *args, **kwargs):
        super().__init__(master, *args, **kwargs)
        self.parent = master
        self.APP = app
        self.setup()

        # 添加测试用log
        # self.fake_add_record()

    def setup(self):
        self.bg_color = "#FFFFFF"
        self.configure(bg=self.bg_color, width=30, height=5)

        # 创建竖直滚动条
        self.scrollbar = tk.Scrollbar(self, orient=tk.VERTICAL)
        self.scrollbar.pack(side=tk.RIGHT, fill=tk.Y)

        # 将滚动条与列表框关联
        self.configure(yscrollcommand=self.scrollbar.set)
        self.scrollbar.config(command=self.yview)

        self.pack(fill=tk.BOTH, expand=True, side=tk.LEFT)

    # 在listbox中添加记录，并提供当前record的颜色设置
    def add_record(self, record: str, color: str = "black"):
        # 格式化时间
        current_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
        # 格式化log
        log = f"[{current_time}] {record}"
        self.insert(tk.END, log)
        # 自动滚动到最新记录
        self.see(tk.END)
        # 设置当前record的颜色
        self.itemconfig(tk.END, {"fg": color})

    def fake_add_record(self):
        self.add_record("测试用串口配置工具启动")
        self.add_record("测试用串口配置工具启动")
        self.add_record("测试用串口配置工具启动")
        self.add_record("测试用串口配置工具启动", color='pink')
        self.add_record("测试用串口配置工具启动", color='pink')
        self.add_record("测试用串口配置工具启动", color='pink')
        self.add_record("测试用串口配置工具启动")
        self.add_record("测试用串口配置工具启动")
        self.add_record("测试用串口配置工具启动")
        self.add_record("测试用串口配置工具启动", color='pink')
        self.add_record("测试用串口配置工具启动", color='pink')
        self.add_record("测试用串口配置工具启动", color='pink')


class Serial_Config_Frame(tk.Frame):
    def __init__(self, master: tk.LabelFrame, app, *args, **kwargs):
        super().__init__(master, *args, **kwargs)
        self.parent = master
        self.APP = app
        self.MAIN_WINDOW = app.MAIN_WINDOW
        self.list_Coms = list(self.APP.MAIN_WINDOW.SERIAL_PORTS.keys())
        # 添加null
        self.list_Coms.append("null")

        self.load_interface_com_map()
        self.check_com_config()

        self.config_item: dict[str, Serial_Config_Item] = {}
        self.setup()

    def load_interface_com_map(self):
        with open("config/interface_com_map.json", "r", encoding='utf-8') as f:
            self.interface_com_map = json.load(f)

    def check_com_config(self):

        for interface, conf in self.interface_com_map.items():
            if conf["com"] not in self.list_Coms and conf["com"] != "null":
                raise Exception(f"测试接口{interface}的串口{conf["com"]}不在当前电脑的串口列表中")

    def setup(self):
        # 此处读取配置文件，测试接口与对应的串口映射好
        port_row = 1
        port_col = 1
        for interface, com in self.interface_com_map.items():
            self.config_item[interface] = Serial_Config_Item(
                self, self.APP,  interface, com)
            self.config_item[interface].grid(row=port_row, column=port_col)
            port_col += 1

        confirm_row = port_row + 1
        confirm_col = 1
        self._2_1_port_confirm_btn = tk.Button(
            self, text="串口配置确认", width=20, command=self.select_port_confirm)
        self._2_1_port_confirm_btn.grid(
            row=confirm_row, column=confirm_col, columnspan=2, padx=10, pady=20)

        self._2_2_port_cancel_btn = tk.Button(
            self, text="串口配置重置", width=20, command=self.select_port_cancel)
        self._2_2_port_cancel_btn.grid(
            row=confirm_row, column=confirm_col+2, columnspan=2, pady=20)

    def select_port_confirm(self):
        for interface, item in self.config_item.items():
            item.port_confirm()
        self.APP.STEP_MANAGER.step00_to_step01()

    def select_port_cancel(self):
        for interface, item in self.config_item.items():
            item.port_cancel()
        self.APP.STEP_MANAGER.reset_step()


class Serial_Config_Item(tk.Frame):
    def __init__(self, master: Serial_Config_Frame, app,  port_interface: str, port_conf: dict, *args, **kwargs):
        super().__init__(master, *args, **kwargs)
        self.parent = master
        self.APP = app
        self.serial_config: Serial_Config_Type = Serial_Config_Type(
            port=port_conf["com"])
        self.port_interface = port_interface
        self.port_conf = port_conf
        self.setup()

    # 有一个下拉选项，里面展示的是当前电脑扫描到的串口列表

    def setup(self):
        WIDTH = 10
        PADX = 20

        self.config_item_index = tk.Label(
            self, width=WIDTH, padx=PADX, text=f"{self.port_interface}")
        self.config_item_index.pack(side=tk.TOP)

        # 显示发送数据
        self.port_combobox_label = tk.Label(
            self, width=WIDTH, padx=PADX, text=f"{self.port_conf['data']}")
        self.port_combobox_label.pack(side=tk.TOP)

        self.port_combobox = ttk.Combobox(
            self, values=self.parent.list_Coms, width=WIDTH, state='readonly')
        self.port_combobox.pack(side=tk.TOP)

        # 默认disable状态，
        self.port_send_btn = tk.Button(
            self, text="发送数据", state=tk.DISABLED,  command=self.send_data)
        self.port_send_btn.pack(side=tk.TOP)

        # 设置初始配置
        item_index = self.parent.list_Coms.index(self.port_conf["com"])
        self.port_combobox.current(item_index)
        selected_value = self.port_combobox.get()
        self.serial_config.port = selected_value

        self.port_combobox.bind("<<ComboboxSelected>>", self.get_port_value)
        self.port_combobox.pack(side=tk.TOP)

    def get_port_value(self, event):
        # 使用 get() 方法获取 Combobox 当前选项值
        selected_value = self.port_combobox.get()
        self.serial_config.port = selected_value
        app_init_logger.debug(
            f"测试接口{self.port_interface}的串口是: {selected_value}")
        # 也可以将其添加到日志列表中
        self.APP.MAIN_WINDOW._2_3_1log_listbox.add_record(
            f"测试接口{self.port_interface}的串口是: {selected_value}")

    def port_confirm(self):
        if self.serial_config.port != "null":
            app_init_logger.debug(
                f"测试接口{self.port_interface}的串口配置是: {self.serial_config.port}")
            self.serial_communication = SerialCommunication(
                self.serial_config.port)
            self.serial_communication.open_port()
            self.port_send_btn.config(state=tk.NORMAL)
            self.port_combobox.config(state=tk.DISABLED)

    def port_cancel(self):
        if self.serial_config.port != "null":
            app_init_logger.debug(
                f"测试接口{self.port_interface}的串口取消")
            self.serial_communication.close_port()
            self.port_send_btn.config(state=tk.DISABLED)
            self.port_combobox.config(state=tk.NORMAL)

    def send_data(self):
        if self.serial_config.port != "null":
            serial_port_logger.debug(
                f"测试接口{self.port_interface}的串口发送数据: {self.port_conf['data']}")
            self.serial_communication.send_data(self.port_conf['data'])

    def receive_data(self):
        if self.serial_config.port != "null":
            received_data_bytes = self.serial_communication.receive_data()

            # 字符串方式展示字节串
            received_data_hex_string = bytes.hex(received_data_bytes)
            # 添加空格
            received_data = " ".join([received_data_hex_string[i:i+2]
                                     for i in range(0, len(received_data_hex_string), 2)])

            received_data = received_data.upper()

            serial_port_logger.debug(
                f"测试接口{self.port_interface}的串口接收数据: {received_data}")
            return received_data
